package system.train; 

import java.util.LinkedList;
import system.track.Block;
import system.track.Switch;
import system.track.Direction;
import system.track.SwitchAngle;
import system.track.Sensor;
import system.track.IllegalFunctionCallException;

/**
 * This is an enumeration of actions a train takes
 * every time it tripped a sensor.
 * 
 * @author Yasmine K 
 */
public enum RouteActions 
{
   SET_NEXT_SWITCH_180 
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           train.getCurrentBlock().getSwitch(currentDirection).setAngle(SwitchAngle.ONE_EIGHTY_DEGREES);
           return false;
       }
   },

   SET_NEXT_SWITCH_90
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           train.getCurrentBlock().getSwitch(currentDirection).setAngle(SwitchAngle.NINETY_DEGREES);
           return false;
       }
   },

   ACTIVATE_NEXT_BLOCK 
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block nextBlock = train.getCurrentBlock().getNextBlock(currentDirection);
           nextBlock.setDirection(currentDirection, train.getSpeed());
           System.out.println("currentBlock ID: " + train.getCurrentBlock().getID()
                   + " nextBlock ID: " + nextBlock.getID());
           return false;
       }
   },

   ACTIVATE_NEXT_BLOCK_180
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block nextBlock = train.getCurrentBlock()
                   .getSpecificBlock(currentDirection,
                        SwitchAngle.ONE_EIGHTY_DEGREES);
           nextBlock.setDirection(currentDirection, train.getSpeed());
           System.out.println("currentBlock ID: " + train.getCurrentBlock().getID()
                   + " nextBlock ID: " + nextBlock.getID());
           return false;
       }
   },

   ACTIVATE_NEXT_BLOCK_90
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block nextBlock = train.getCurrentBlock()
                   .getSpecificBlock(currentDirection,
                        SwitchAngle.NINETY_DEGREES);
           nextBlock.setDirection(currentDirection, train.getSpeed());
           System.out.println("currentBlock ID: " + train.getCurrentBlock().getID()
                   + " nextBlock ID: " + nextBlock.getID());
           return false;
       }
   },

   RESERVE_ACTIVATE_NEXT_BLOCK
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block currentBlock = train.getCurrentBlock();
           Block nextBlock = currentBlock.getNextBlock(currentDirection);
           if (!nextBlock.reserve())
           {
               currentBlock.setDirection(currentDirection, 0);
               while (!nextBlock.reserve())
               {
                   try
                   {
                        Thread.sleep(300);
                   }
                   catch (InterruptedException ex)
                   {
                       
                   }
               }
               currentBlock.setDirection(currentDirection, train.getSpeed());
           }
            return RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(train);
       }
   },

   RESERVE_ACTIVATE_NEXT_BLOCK_180
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block currentBlock = train.getCurrentBlock();
           Block nextBlock = currentBlock
                   .getSpecificBlock(currentDirection,
                        SwitchAngle.ONE_EIGHTY_DEGREES);
           if (!nextBlock.reserve())
           {
               currentBlock.setDirection(currentDirection, 0);
               while (!nextBlock.reserve())
               {
                   try
                   {
                        Thread.sleep(300);
                   }
                   catch (InterruptedException ex)
                   {

                   }
               }
               currentBlock.setDirection(currentDirection, train.getSpeed());
           }
            return RouteActions.ACTIVATE_NEXT_BLOCK_180.executeAction(train);
       }
   },

   RESERVE_ACTIVATE_NEXT_BLOCK_90
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block currentBlock = train.getCurrentBlock();
           Block nextBlock = currentBlock
                   .getSpecificBlock(currentDirection,
                        SwitchAngle.NINETY_DEGREES);
           if (!nextBlock.reserve())
           {
               currentBlock.setDirection(currentDirection, 0);
               while (!nextBlock.reserve())
               {
                   try
                   {
                        Thread.sleep(300);
                   }
                   catch (InterruptedException ex)
                   {

                   }
               }
               currentBlock.setDirection(currentDirection, train.getSpeed());
           }
            return RouteActions.ACTIVATE_NEXT_BLOCK_90.executeAction(train);
       }
   },

   DEACTIVATE_PREVIOUS_BLOCK 
   {
       boolean executeAction(Train train)
       {
           train.getCurrentBlock().setDirection(Direction.CLOCKWISE, 0);
           train.getCurrentBlock().unreserve();
           return false;
       }
   },

   MOVE_TRAIN_AHEAD
   {
        boolean executeAction(Train train)
        {
            Direction currentDirection = train.getDirection();
            Block nextBlock = train.getCurrentBlock().getNextBlock(currentDirection);
            System.out.println("Moving train to block: " + nextBlock.getID());
            train.setCurrentBlock(nextBlock);
            return false;
        }
   },
   
   ACTIVATE_CURRENT_BLOCK
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           train.getCurrentBlock().setDirection(currentDirection, train.getSpeed());
           return false;
       }
   },

   RESERVE_ACTIVATE_CURRENT_BLOCK
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           Block currentBlock = train.getCurrentBlock();
           if (!currentBlock.reserve())
           {
               while (!currentBlock.reserve())
               {
                   try
                   {
                        Thread.sleep(300);
                   }
                   catch (InterruptedException ex)
                   {

                   }
               }
               currentBlock.setDirection(currentDirection, train.getSpeed());
           }
           return RouteActions.ACTIVATE_CURRENT_BLOCK.executeAction(train);
       }
   },
   
   SET_BLOCK_TO_PARK 
   {
       boolean executeAction(Train train)
       {
           try
           {
               train.getCurrentBlock().setPark(true);
           }
           catch(IllegalFunctionCallException e) 
           {
           }
           return false;
       }
   },

   SET_BLOCK_NOT_TO_PARK 
   {
       boolean executeAction(Train train)
       {
           try
           {
               train.getCurrentBlock().setPark(false);
           }
           catch(IllegalFunctionCallException e) 
           {
           }
           return false;
       }
   },

   REGISTER_SENSORS 
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           LinkedList<Sensor> sensors = train.getCurrentBlock().getSensors(currentDirection);
           for(Sensor asensor: sensors)
           {
               asensor.registerForNotification(train);
           }
           return false;
       }
   },

   UNREGISTER_SENSORS 
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           LinkedList<Sensor> sensors = train.getCurrentBlock().getSensors(currentDirection);
           for(Sensor asensor: sensors)
           {
               asensor.unregisterForNotification(train);
           }   //Also done already for the sensor triggered.
               //This is a hack because of hand controller issues.
               //However, because we can register for 2 sensors at once,
               //we still need to ensure complete unregistering.

           return false;
       }
   },

   UNREGISTER_NEXT_SENSORS
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           LinkedList<Sensor> sensors = train.getCurrentBlock().getNextBlock(currentDirection).getSensors(currentDirection);
           for(Sensor asensor: sensors)
           {
               asensor.unregisterForNotification(train);
           }
           return false;
       }
   },

   TOGGLE_DIRECTION
   {
       boolean executeAction(Train train)
       {
           Direction currentDirection = train.getDirection();
           switch(currentDirection)
           {
                case CLOCKWISE:
                    train.setDirection(Direction.COUNTERCLOCKWISE);
                    break;
                case COUNTERCLOCKWISE:
                    train.setDirection(Direction.CLOCKWISE);
                    break;
           }
           return false;
       } 
   },
    
   WAIT_FOR_SENSOR 
   {
       boolean executeAction(Train train)
       {
           return true;
       }
   },

   STOP 
   {
        @Override
       boolean executeAction(Train train)
       {
           train.getCurrentBlock().setDirection(Direction.CLOCKWISE,0);
           return false;
       }
   },

   START
   {
       boolean executeAction(Train train)
       {
           train.getCurrentBlock().setDirection(
                   train.getDirection(), train.getSpeed());
           return false;
       }
   },

   CONTINUE
   {
       boolean executeAction(Train train)
       {
           RouteActions.START.executeAction(train);
           RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(train);
           return false;
       }
   },

   ACTIVATE_PARKING_BLOCK_90
   {
       boolean executeAction(Train train)
       {
           boolean returnVal = false;
           Direction d = train.getDirection();
           Block currentBlock = train.getCurrentBlock();
           Block nextBlock = currentBlock
                   .getSpecificBlock(d, SwitchAngle.NINETY_DEGREES);
           if (nextBlock.getID() == 4)
           {
               if (!nextBlock.reserve())
               {
                   currentBlock.setDirection(d, 0);
                   while(!nextBlock.reserve()) { }
                   currentBlock.setDirection(d, train.getSpeed());

               }
               switch(d)
               {
                   case CLOCKWISE:
                       nextBlock.setDirection(Direction.COUNTERCLOCKWISE,
                               train.getSpeed());
                       break;
                   case COUNTERCLOCKWISE:
                       nextBlock.setDirection(Direction.CLOCKWISE,
                               train.getSpeed());
                       break;
                   default:
                }
           } else {
               returnVal = RouteActions.ACTIVATE_NEXT_BLOCK.executeAction(train);
           }
           return returnVal;
       }
   };
   
   abstract boolean executeAction(Train train);
}
